package checkers;

import ai.State;
import java.util.ArrayList;
import java.util.List;

public class CheckersState implements State {

    public static final byte BLANK_POSITION = 0;
    public static final byte WHITE_COMMON = 2;
    public static final byte WHITE_KING = 3;
    public static final byte BLACK_COMMON = 8;
    public static final byte BLACK_KING = 9;
    public static final byte WHITE_PLAYER = 1;
    public static final byte BLACK_PLAYER = 2;
    private byte[][] board;
    private byte currentPlayer;
    private byte whitePiecesAmount;
    private byte blackPiecesAmount;

    public CheckersState(byte currentPlayer) {
        this.currentPlayer = currentPlayer;
    }

    public void refreshPiecesAmount() {
        whitePiecesAmount = 0;
        blackPiecesAmount = 0;
        for (int i = 0; i < 8; i++) {
            for (int j = (i % 2 == 0) ? 1 : 0; j < 8; j += 2) {
                switch (this.board[i][j]) {
                    case WHITE_COMMON:
                        whitePiecesAmount++;
                        break;
                    case WHITE_KING:
                        whitePiecesAmount++;
                        break;
                    case BLACK_COMMON:
                        blackPiecesAmount++;
                        break;
                    case BLACK_KING:
                        blackPiecesAmount++;
                        break;
                }
            }
        }
    }

    public byte[][] getBoard() {
        return board;
    }

    public void setBoard(byte[][] board) {
        this.board = board;
    }

    public byte getCurrentPlayer() {
        return currentPlayer;
    }

    public CheckersState clone() {
        CheckersState target = new CheckersState(this.currentPlayer);
        byte[][] board = new byte[this.board.length][this.board.length];
        for (short i = 0; i < board.length; i++) {
            for (short j = 0; j < board.length; j++) {
                board[i][j] = this.board[i][j];
            }
        }
        target.board = board;
        target.whitePiecesAmount = this.whitePiecesAmount;
        target.blackPiecesAmount = this.blackPiecesAmount;
        return target;
    }

    public void applyMovement(CheckersMovement movement) {
        byte piece = 0;
        CheckersOperation lastPos = null;
        for (CheckersOperation op : movement.getMovementsList()) {
            switch (op.operation) {
                case CheckersOperation.SOURCE_POSITION:
                    piece = this.board[op.x][op.y];
                    if (lastPos != null) {
                        this.board[lastPos.x][lastPos.y] = CheckersState.BLANK_POSITION;
                    }
                    lastPos = op;
                    break;
                case CheckersOperation.TARGET_POSITION:
                    if (lastPos != null) {
                        this.board[lastPos.x][lastPos.y] = CheckersState.BLANK_POSITION;
                    }
                    lastPos = op;
                    this.board[op.x][op.y] = piece;
                    break;
                case CheckersOperation.REMOVE_PIECE:
                    if (this.board[op.x][op.y] == CheckersState.WHITE_COMMON
                            || this.board[op.x][op.y] == CheckersState.WHITE_KING) {
                        this.whitePiecesAmount--;
                    }
                    if (this.board[op.x][op.y] == CheckersState.BLACK_COMMON
                            || this.board[op.x][op.y] == CheckersState.BLACK_KING) {
                        this.blackPiecesAmount--;
                    }
                    this.board[op.x][op.y] = CheckersState.BLANK_POSITION;
                    break;
                case CheckersOperation.BECOME_KING:
                    switch (this.board[op.x][op.y]) {
                        case CheckersState.WHITE_COMMON:
                            this.board[op.x][op.y] = CheckersState.WHITE_KING;
                            break;
                        case CheckersState.BLACK_COMMON:
                            this.board[op.x][op.y] = CheckersState.BLACK_KING;
                            break;
                    }
                    break;
            }
        }
        this.currentPlayer = (this.currentPlayer == WHITE_PLAYER ? BLACK_PLAYER : WHITE_PLAYER);
    }

    public float getUtilityValue(byte maxPlayer) {
        if (whitePiecesAmount == 0) {
            if (maxPlayer == WHITE_PLAYER) {
                return Float.NEGATIVE_INFINITY;
            } else {
                return Float.POSITIVE_INFINITY;
            }
        }

        if (blackPiecesAmount == 0) {
            if (maxPlayer == WHITE_PLAYER) {
                return Float.POSITIVE_INFINITY;
            } else {
                return Float.NEGATIVE_INFINITY;
            }
        }

        if (CheckersProblem.avaiableMovements(this).size() == 0) {
            if (this.currentPlayer == maxPlayer) {
                return Float.NEGATIVE_INFINITY;
            } else {
                return Float.POSITIVE_INFINITY;
            }
        } else {
            this.invertPlayer();
            if (CheckersProblem.avaiableMovements(this).size() == 0) {
                this.invertPlayer();
                if (this.currentPlayer == maxPlayer) {
                    return Float.NEGATIVE_INFINITY;
                } else {
                    return Float.POSITIVE_INFINITY;
                }
            }
            this.invertPlayer();
        }
        return 0;
    }

    private void invertPlayer() {
        this.currentPlayer = (this.currentPlayer == WHITE_PLAYER ? BLACK_PLAYER : WHITE_PLAYER);
    }

    @Override
    public List<State> getChildren() {
        List<CheckersMovement> avaiableMovements = CheckersProblem.avaiableMovements(this);
        List<State> list = new ArrayList<State>();
        for (CheckersMovement moviment : avaiableMovements) {
            CheckersState clone = this.clone();
            clone.applyMovement(moviment);
            list.add(clone);
        }
        return list;
    }

    @Override
    public float heuristicValue(byte maxPlayer) {
        int whiteCommonPiecesAmount = 0;
        int whiteKingPiecesAmount = 0;
        int whiteAvaiableMoviments = 0;
        int blackCommonPiecesAmount = 0;
        int blackKingPiecesAmount = 0;
        int blackAvaiableMoviments = 0;

        for (int i = 0; i < 8; i++) {
            for (int j = (i % 2 == 0) ? 1 : 0; j < 8; j += 2) {
                switch (this.board[i][j]) {
                    case WHITE_COMMON:
                        whiteCommonPiecesAmount++;
                        break;
                    case WHITE_KING:
                        whiteKingPiecesAmount++;
                        break;
                    case BLACK_COMMON:
                        blackCommonPiecesAmount++;
                        break;
                    case BLACK_KING:
                        blackKingPiecesAmount++;
                        break;
                }
            }
        }

        if (this.currentPlayer == WHITE_PLAYER) {
            whiteAvaiableMoviments = CheckersProblem.avaiableMovements(this).size();
            this.invertPlayer();
            blackAvaiableMoviments = CheckersProblem.avaiableMovements(this).size();
            this.invertPlayer();
        } else {
            blackAvaiableMoviments = CheckersProblem.avaiableMovements(this).size();
            this.invertPlayer();
            whiteAvaiableMoviments = CheckersProblem.avaiableMovements(this).size();
            this.invertPlayer();
        }
        if (this.currentPlayer == WHITE_PLAYER) {
            return 6 * (whiteKingPiecesAmount - blackKingPiecesAmount)
                    + 4 * (whiteCommonPiecesAmount - blackCommonPiecesAmount)
                    + (whiteAvaiableMoviments - blackAvaiableMoviments);
        } else {
            return 6 * (blackKingPiecesAmount - whiteKingPiecesAmount)
                    + 4 * (blackCommonPiecesAmount - whiteCommonPiecesAmount)
                    + (blackAvaiableMoviments - whiteAvaiableMoviments);
        }


    }
}
